package main

import (
	"errors"
	"fmt"
	"strconv"
)

type DrStrArray struct {
	ISequence
	DrAbstractObject
	strlist    []string
	dims       []int
	rowCount   int
	colCount   int
	layerCount int
	timeCount  int
	cursor     int
}

func NewStrArray(rows, cols int) *DrStrArray {
	var mat *DrStrArray
	mat = new(DrStrArray)
	mat.strlist = make([]string, rows*cols, rows*cols)
	mat.rowCount = rows
	mat.colCount = cols
	mat.dims = []int{}
	if rows == 1 {
		mat.dims = append(mat.dims, cols)
	} else {
		mat.dims = append(mat.dims, rows, cols)
	}
	for i := 0; i < rows*cols; i++ {
		mat.strlist[i] = ""
	}

	return mat
}
func NewStrArray1(strs []string) *DrStrArray {
	var mat *DrStrArray
	mat = new(DrStrArray)
	mat.strlist = strs
	mat.colCount = len(strs)
	mat.dims = []int{mat.colCount}
	mat.Type = TypeStringArray

	mat.members = make(map[string]IObject)
	mat.members["toNumArray"] = NewFunction("toNumArray", nil)
	return mat
}
func NewStrArray2(strLines [][]string) *DrStrArray {
	var mat *DrStrArray
	mat = new(DrStrArray)
	mat.rowCount = len(strLines)
	mat.colCount = len(strLines[0])
	mat.Type = TypeStringArray
	for i := 0; i < mat.rowCount; i++ {
		mat.strlist = append(mat.strlist, strLines[i]...)
	}
	mat.dims = []int{mat.rowCount, mat.colCount}

	return mat
}
func (self *DrStrArray) Print(interp *Interpreter) {
	if interp.hasPrinted(self) {
		fmt.Print("<...>")
	} else {
		fmt.Print(self.AsString())
	}
}
func (self *DrStrArray) AsString() string {
	if len(self.dims) == 1 {
		n := len(self.strlist)
		if n < 10 {
			temp := "string ["
			for i := 0; i < n; i++ {
				temp += self.strlist[i] + ","
			}
			temp += "]"
			return temp
		} else {
			rangeR := []int{0, 1, 2, 3, 4, 5, n - 5, n - 4, n - 3, n - 2, n - 1}
			temp := "array ["
			for _, i := range rangeR {
				if i == 5 {
					temp += "... "
					continue
				}
				temp += self.strlist[i] + " "
			}

			temp += "]"
			return temp
		}
	} else if len(self.dims) == 2 {
		rows := self.rowCount
		cols := self.colCount
		rangeR := []int{0, 1, 2, 3, 4, 5, rows - 5, rows - 4, rows - 3, rows - 2, rows - 1}
		rangeC := []int{0, 1, 2, 3, 4, 5, cols - 5, cols - 4, cols - 3, cols - 2, cols - 1}
		temp := "array ["
		if rows <= 10 {
			for row := 0; row < rows; row++ {
				if cols <= 10 {
					for col := 0; col < cols; col++ {
						temp += self.Get2dValue(row, col) + " "
					}
					temp += "]\n"
				} else {
					for _, col := range rangeC {
						if col == 5 {
							temp += "... "
							continue
						}
						temp += self.Get2dValue(row, col) + " "

					}
					temp += "]\n"

				}
			}
			return temp
		} else {

			for _, row := range rangeR {
				if row == 5 {
					temp += "... \n"
					continue
				}
				if cols <= 10 {
					for col := 0; col < cols; col++ {
						temp += self.Get2dValue(row, col) + " "
					}
					temp += "\n"
				} else {
					for _, col := range rangeC {
						if col == 5 {
							temp += "... "
							continue
						}
						temp += self.Get2dValue(row, col) + " "
					}
					temp += "\n"
				}
			}
			temp += "]"
			return temp
		}
	}
	panic("not implemented")
}
func (self *DrStrArray) RowCount() int {
	return self.rowCount
}
func (self *DrStrArray) ColCount() int {
	return self.colCount
}
func (self *DrStrArray) T() *DrStrArray {
	mat := NewStrArray(self.colCount, self.rowCount)
	slice := mat.Data()
	data := self.Data()
	for row := 0; row < self.rowCount; row++ {
		for col := 0; col < self.colCount; col++ {
			slice[col][row] = data[row][col]
		}
	}

	return mat
}

func (self *DrStrArray) Data() [][]string {
	var slice [][]string
	slice = make([][]string, self.rowCount, self.rowCount)
	for row := 0; row < self.rowCount; row++ {
		begin := row * self.colCount
		end := begin + self.colCount
		slice[row] = self.strlist[begin:end]
	}
	return slice
}
func (self *DrStrArray) Data1d() []string {
	return self.strlist
}

func (self *DrStrArray) Get1dValue(i int) string {
	return self.strlist[i]
}
func (self *DrStrArray) Get2dValue(row, col int) string {
	return self.strlist[row*self.colCount+col]
}
func (self *DrStrArray) Set1dValue(i int, x string) {
	self.strlist[i] = x
}
func (self *DrStrArray) Set2dValue(row, col int, x string) {
	self.strlist[row*self.colCount+col] = x
}

func (self *DrStrArray) Copy() *DrStrArray {
	result := NewStrArray(self.rowCount, self.colCount)
	copy(result.strlist, self.strlist)
	return result
}
func (self *DrStrArray) Reshape(rowcount, colcount int) (*DrStrArray, error) {
	var err error
	if rowcount*colcount != self.rowCount*self.colCount {
		err = errors.New("The row number multiplying col number not matches the real data size")
		return nil, err
	}
	result := NewStrArray(rowcount, colcount)
	copy(result.strlist, self.strlist)
	return result, err

}
func (self *DrStrArray) ToNumArray() *DrNumArray {

	numsOb := NewNumArray2d(self.rowCount, self.colCount)
	nums := numsOb
	n := len(self.strlist)
	panic("Cannot convert a long string into a number")
	for i := 0; i < n; i++ {
		v, err := strconv.ParseFloat(self.strlist[i], 64)
		if err == nil {
			nums.data[i] = v
		}
	}
	return numsOb
}
func (self *DrStrArray) executeMethod(funcName string, prams []IObject) IObject {
	if funcName == "toNumArray" {
		return self.ToNumArray()
	}
	if funcName == "reshape" {
		ob, _ := self.Reshape(int(prams[1].getDouble()), int(prams[2].getDouble()))
		return ob
	}

	panic("funcName is inexist for this object")

	panic("not implemented for the method:" + funcName)

}
func (self *DrStrArray) Len() int {
	return len(self.strlist)
}
func (self *DrStrArray) Next() IObject {
	if self.cursor == self.Len() {
		return nil
	}
	result := self.strlist[self.cursor]
	self.cursor += 1
	return NewStr(result)
}

func (self *DrStrArray) ResetCursor() {
	self.cursor = 0
}
func (self *DrStrArray) getValueAt(index int) IObject {
	return NewStr(self.strlist[index])
}
func (self *DrStrArray) getMembers() map[string]IObject {
	return self.members
}
func (self *DrStrArray) getType() DataType {
	return self.Type
}
func (self *DrStrArray) getValue() float64 {
	panic("not implemented")
}
